package org.speed.jdk;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;

public class SlideWindowDemo {
	
	static class SlideWindow {
		
		private Map<String, LimitConf> limitMap = new HashMap<String, SlideWindowDemo.LimitConf>();
		
		private StatisCount[] ringBuffer = new StatisCount[] {
			new StatisCount(),new StatisCount(),new StatisCount(),
			new StatisCount(),new StatisCount()	,new StatisCount()
		};
		
		@SuppressWarnings("deprecation")
		boolean isLimit(String method) {
			if(limitMap.get(method) == null ) {
				return true;
			}
			
			Long time = System.currentTimeMillis() / 1000 * 1000;
			Date date = new Date(time);
			int seconds = date.getSeconds();
			int index = seconds / 10;
			date.setSeconds(index * 10);
			
			LimitConf conf = limitMap.get(method);
			int requestCount = 0;
			for (int i = 0; i < ringBuffer.length; i++) {
				StatisCount temp = ringBuffer[i];
				if(temp.getTime() != 0 && date.getTime() - temp.getTime() < 60 * 1000 ) {
					requestCount+=temp.getSucc().get();
				}
			}
			
			StatisCount count = ringBuffer[index];
			if(date.getTime() - count.getTime() >= 60 * 1000 ) {
				count.setSucc(new AtomicInteger(0));
			}else {
				count.getSucc().getAndIncrement();
			};
			count.setTime(date.getTime());
			
			if(requestCount + 1 > conf.getThresold() ) {
				return false;
			}
			
			return true;
		}

		public Map<String, LimitConf> getLimitMap() {
			return limitMap;
		}

		public void setLimitMap(Map<String, LimitConf> limitMap) {
			this.limitMap = limitMap;
		}
		
	}
	
	
	static class StatisCount {
		
		private Long time = 0L;
		private AtomicInteger succ = new AtomicInteger(0);
		private AtomicInteger error = new AtomicInteger(0);
		private AtomicInteger timeout = new AtomicInteger(0);
		public Long getTime() {
			return time;
		}
		public void setTime(Long time) {
			this.time = time;
		}
		public AtomicInteger getSucc() {
			return succ;
		}
		public void setSucc(AtomicInteger succ) {
			this.succ = succ;
		}
		public AtomicInteger getError() {
			return error;
		}
		public void setError(AtomicInteger error) {
			this.error = error;
		}
		public AtomicInteger getTimeout() {
			return timeout;
		}
		public void setTimeout(AtomicInteger timeout) {
			this.timeout = timeout;
		}
		@Override
		public String toString() {
			SimpleDateFormat sdfDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			return "=====>[time=" + sdfDateFormat.format(new Date(time)) + ", succ=" + succ + "]";
		}
		
	}
	
	static class LimitConf {
		
		private String method;
		
		private int thresold;
		
		public LimitConf(String method, int thresold) {
			super();
			this.method = method;
			this.thresold = thresold;
		}

		public String getMethod() {
			return method;
		}

		public void setMethod(String method) {
			this.method = method;
		}

		public int getThresold() {
			return thresold;
		}

		public void setThresold(int thresold) {
			this.thresold = thresold;
		}
		
	}
	
	
	static class RequestService {
		SlideWindow slideWindow;
		
		public RequestService(SlideWindow slideWindow) {
			this.slideWindow = slideWindow;
		}


		void payOrder() {
			try {
				Thread.sleep(new Random().nextInt(1000 * 5 ));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
			if(!slideWindow.isLimit("payOrder")) {
				System.out.println("limit payOrder..");
			}else {
				//System.out.println("payOrder run.....");
			}
			System.out.println(Arrays.asList(slideWindow.ringBuffer));

		}
		
	}
	
	
	
	
	public static void main(String[] args) {
		
		SlideWindow slideWindow = new SlideWindow();
		Map<String, LimitConf> limitMap = new HashMap<String, SlideWindowDemo.LimitConf>();
		LimitConf conf = new LimitConf("payOrder", 10);
		limitMap.put("payOrder", conf);
		slideWindow.setLimitMap(limitMap);
		
		RequestService requestService = new RequestService(slideWindow);
		for (int i = 0; i < 100000; i++) {
			requestService.payOrder();
		}
		
	}

}
